home *** CD-ROM | disk | FTP | other *** search
/ Ham Radio 2000 #2 / Ham Radio 2000 - Volume 2.iso / HAMV2 / TCP_IP / TNOS230S / MAILFOR.C < prev    next >
Encoding:
C/C++ Source or Header  |  1997-09-07  |  14.9 KB  |  699 lines

  1. /* The following code will broadcast 'Mail for:' beacons
  2.  * for private users with unread mail on a regular interval
  3.  *
  4.  * Also contains the commands to set the R: header read options
  5.  * with the 'bulletin' command.
  6.  *
  7.  * original: 920306
  8.  * rewritten: 920326
  9.  * Copyright 1992, Johan. K. Reinalda, WG7J/PA3DIS
  10.  * Permission granted for non-commercial use only!
  11.  */
  12. #include "global.h"
  13. #include "commands.h"
  14. #ifdef MSDOS
  15. #include "socket.h"
  16. #endif
  17. #include "files.h"
  18. #include "dirutil.h"
  19. #include "bm.h"
  20. #include "x.h"
  21.  
  22.  
  23. #if !defined(_lint)
  24. static char rcsid[] OPTIONAL = "$Id: mailfor.c,v 1.32 1997/09/07 21:18:28 root Exp root $";
  25. #endif
  26.  
  27. #ifdef MAILFOR
  28. #ifdef AX25
  29.  
  30. #define MAXMFLEN 256
  31. static struct timer Mftimer;
  32. static char ax_mftext[MAXMFLEN + 1] = "Mail for:";
  33.  
  34. #define DEFMFLEN 9
  35. static int mflen = DEFMFLEN;        /*Initial lenght of mail-for string*/
  36.  
  37. #ifdef HOLDMONITOR
  38. #ifdef SOUNDS
  39. char *HOLDsound;
  40. #endif
  41. int HOLDmode, HOLDindicator;
  42. #endif
  43.  
  44. #ifdef ALERTMONITOR
  45. #ifdef SOUNDS
  46. static char *ALERTsound;
  47. #endif
  48. static int ALERTmode;
  49. int ALERTindicator;
  50. #endif
  51.  
  52. extern int CountConfUsers (void);
  53. static int checknewmail (char *area);
  54. static int mf_exclude (char *name);
  55. static void countMessages (void);
  56. static int setmailfor (void);
  57. static void ax_mf (struct iface * ifp);
  58. static void Mftick (void *v);
  59. int dombmailfor (int argc, char **argv, void *p);
  60. int indexFwdBbs (char *name);
  61.  
  62.  
  63. static struct no_mf *No_mf = NULLMF;
  64.  
  65. #ifdef ALERTMONITOR
  66. static struct mfalert *mf_alerts = NULLMFA;
  67. #endif
  68.  
  69.  
  70. /*Read a private message area's control file, searching for unread mail
  71.  *this is indicated by the status int
  72.  */
  73.  
  74. static int
  75. checknewmail (char *area)
  76. {
  77. FILE *fp;
  78. char mailbox[100];
  79. long size;
  80. int i, nmsgs;
  81. struct let *cmsg;
  82. struct let *new;
  83. int ret = 0;
  84.  
  85.     sprintf (mailbox, "%s/control/%s.ctl", Mailspool, area);
  86.     if ((fp = fopen (mailbox, READ_BINARY)) != NULLFILE) {
  87.         size = (long) filelength (fileno (fp));
  88.         new = (struct let *) mallocw ((unsigned int) size);
  89.         kwait (NULL);
  90.         (void) fread (new, (unsigned int) size, 1, fp);
  91.         (void) fclose (fp);
  92.         kwait (NULL);
  93.         nmsgs = (int) (size / (long) sizeof (struct let));
  94.  
  95.         for (cmsg = new, i = 0; i < nmsgs; i++, cmsg++) {
  96.             if (!(cmsg->status & BM_READ)) {
  97.                 ret = 1;
  98.                 break;
  99.             }
  100.         }
  101.         free (new);
  102.     }
  103.     return ret;
  104. }
  105.  
  106.  
  107. /* Check name with exclude list;
  108.  * returns 1 if found, 0 if not
  109.  */
  110. static int
  111. mf_exclude (char *name)
  112. {
  113. struct no_mf *nm;
  114.  
  115.     /*Now check the 'exclude' list*/
  116.     for (nm = No_mf; nm != NULLMF; nm = nm->next) {
  117.         if (!stricmp (nm->area, name))
  118.             return 1;
  119.     }
  120.     return 0;
  121. }
  122.  
  123.  
  124. #ifdef CONVERS
  125. static char ActiveMessages[] = "Active Messages: %ld  (%s in conference sessions)";
  126. #else
  127. static char ActiveMessages[] = "Active Messages: %ld";
  128. #endif
  129. static long ACTIVEMessages = 0;
  130.  
  131. static void
  132. countMessages (void)
  133. {
  134. char buf[128];
  135. struct ffblk ff;
  136. long size;
  137.  
  138.     ACTIVEMessages = 0;
  139.     sprintf (buf, "%s/control/*.ctl", Mailspool);
  140.     if (findfirst (buf, &ff, 0) == 0) {
  141.         do {
  142.             kwait (NULL);    /* Let others run */
  143.             sprintf (buf, "%s/control/%s", Mailspool, ff.ff_name);
  144.             size = fsize (buf);
  145.             ACTIVEMessages += (size / 14L);
  146.         } while (findnext (&ff) == 0);
  147.     }
  148. }
  149.  
  150.  
  151. static int
  152. setmailfor (void)
  153. {
  154. char buf[80];
  155. struct ffblk ff;
  156. char *cp;
  157.  
  158.     sprintf (buf, "%s/*.txt", Mailspool);
  159.     if (findfirst (buf, &ff, 0) == 0) {
  160.         do {
  161.             kwait (NULL);    /* Let others run */
  162.             cp = strrchr (ff.ff_name, '.');
  163.             if (cp)        /* should always be true */
  164.                 *cp = '\0';
  165.             /*must be private mail area, and not on exclude list !*/
  166.             if (!issysarea (ff.ff_name) && !mf_exclude (ff.ff_name)) {
  167.                 if ((strlen (ax_mftext) + strlen (ff.ff_name)) > MAXMFLEN - 1)
  168.                     break;    /* That's all folks */
  169.                 if (checknewmail (ff.ff_name)) {
  170.                     strcat (ax_mftext, " ");
  171.                     strcat (ax_mftext, ff.ff_name);
  172.                 }
  173.             }
  174.         } while (findnext (&ff) == 0);
  175.     }
  176.     countMessages ();
  177.  
  178.     return (int) strlen (ax_mftext);
  179. }
  180.  
  181.  
  182. /*This is the low-level broadcast function.*/
  183. static void
  184. ax_mf (struct iface *ifp)
  185. {
  186. struct mbuf *bp;
  187. #ifdef CONVERS
  188. int inconference = 0;
  189. char fakestr[8];
  190. #endif
  191.  
  192.     /* prepare the header */
  193.     if ((bp = alloc_mbuf ((int16) mflen)) == NULLBUF)
  194.         return;
  195.  
  196.     /*copy the data into the packet*/
  197.     bp->cnt = (int16) mflen;
  198.     memcpy (bp->data, ax_mftext, (unsigned int) mflen);
  199.  
  200.     /* send it */
  201.     (void) (*ifp->output) (ifp, Ax25multi[MAILCALL], ifp->hwaddr, PID_NO_L3, bp);
  202.  
  203.  
  204.     /* prepare the header */
  205.     if ((bp = alloc_mbuf ((int16) (strlen (ActiveMessages) + 16))) == NULLBUF)
  206.         return;
  207.  
  208.     /*copy the data into the packet*/
  209. #ifdef CONVERS
  210.     inconference = CountConfUsers ();
  211.     sprintf ((char *) bp->data, ActiveMessages, ACTIVEMessages, (inconference) ? itoa (inconference, fakestr, 10) : "NONE");
  212. #else
  213.     sprintf (bp->data, ActiveMessages, ACTIVEMessages);
  214. #endif
  215.     bp->cnt = (int16) strlen ((char *) bp->data);
  216.  
  217.     /* send it */
  218.     (void) (*ifp->output) (ifp, Ax25multi[MAILCALL], ifp->hwaddr, PID_NO_L3, bp);
  219. }
  220.  
  221.  
  222. static void
  223. Mftick (void *v OPTIONAL)
  224. {
  225. struct iface *ifp = Ifaces;
  226.  
  227.     stop_timer (&Mftimer);    /* in case this was 'kicked' with a 'mailfor now'*/
  228.  
  229.     /* Now find private mail areas with unread mail. add these to the info-line */
  230.     ax_mftext[DEFMFLEN] = '\0';    /* Back to only 'Mail for:' again*/
  231.  
  232.     if ((mflen = setmailfor ()) < DEFMFLEN + 1) {
  233.         start_detached_timer (&Mftimer);
  234.         return;        /* No unread mail */
  235.     }
  236.  
  237.     /*broadcast it on all configured interfaces*/
  238.     for (ifp = Ifaces; ifp != NULL; ifp = ifp->next)
  239.         if (ifp->flags & MAIL_BEACON)
  240.             ax_mf (ifp);
  241.     /* Restart timer */
  242.     start_detached_timer (&Mftimer);
  243. }
  244.  
  245.  
  246. #ifdef ALERTMONITOR
  247. static int domfalert (int argc, char *argv[], void *p);
  248. #endif
  249. static int domfexclude (int argc, char *argv[], void *p);
  250. #ifdef HOLDMONITOR
  251. static int domfhold (int argc, char *argv[], void *p);
  252. #endif
  253. static int domfnow (int argc, char *argv[], void *p);
  254. int domflist (int argc, char *argv[], void *p);
  255.  
  256.  
  257. static struct cmds MFORtab[] =
  258. {
  259. #ifdef ALERTMONITOR
  260.     { "alert",    domfalert,    0, 0, NULLCHAR },
  261. #endif
  262.     { "exclude",    domfexclude,    0, 0, NULLCHAR },
  263. #ifdef HOLDMONITOR
  264.     { "hold",    domfhold,    0, 0, NULLCHAR },
  265. #endif
  266.     { "listing",    domflist,    0, 0, NULLCHAR },
  267.     { "now",    domfnow,      256, 0, NULLCHAR },
  268.     { "timer",    domftimer,    0, 0, NULLCHAR },
  269.     { NULLCHAR,    NULL,        0, 0, NULLCHAR }
  270. };
  271.  
  272.  
  273. int
  274. dombmailfor (int argc, char *argv[], void *p)
  275. {
  276.     return subcmd (MFORtab, argc, argv, p);
  277. }
  278.  
  279.  
  280. int
  281. domflist (int argc OPTIONAL, char *argv[] OPTIONAL, void *p OPTIONAL)
  282. {
  283.     if (mflen > DEFMFLEN)
  284.         tprintf ("%s\n", ax_mftext);
  285.     return 0;
  286. }
  287.  
  288.  
  289. int
  290. domftimer (int argc, char *argv[], void *p OPTIONAL)
  291. {
  292. #ifdef CONVERS
  293. int inconference = 0;
  294. char fakestr[8];
  295. #endif
  296.  
  297.     if (argc < 2) {        /* set the timer */
  298.         tprintf ("Mail-for timer: %lu/%lu\n", read_timer (&Mftimer) / 1000L,
  299.              dur_timer (&Mftimer) / 1000L);
  300.         if (ACTIVEMessages) {
  301. #ifdef CONVERS
  302.             inconference = CountConfUsers ();
  303.             tprintf (ActiveMessages, ACTIVEMessages, (inconference) ? itoa (inconference, fakestr, 10) : "NONE");
  304. #else
  305.             tprintf (ActiveMessages, ACTIVEMessages);
  306. #endif
  307.             tputs ("\n");
  308.         }
  309.         if (mflen > DEFMFLEN)
  310.             tprintf ("%s\n", ax_mftext);
  311.         tputs ("\n");
  312.         return 0;
  313.     }
  314.     Mftimer.func = (void (*)(void *)) Mftick;    /* what to call on timeout */
  315.     Mftimer.arg = NULL;                    /* dummy value */
  316.     set_timer (&Mftimer, atol (argv[1]) * 1000L);        /* set timer duration */
  317.     start_detached_timer (&Mftimer);
  318.     return 0;
  319. }
  320.  
  321.  
  322. int
  323. domfnow (int argc OPTIONAL, char *argv[] OPTIONAL, void *p OPTIONAL)
  324. {
  325.     Mftick (NULL);
  326.     return 0;
  327. }
  328.  
  329.  
  330. int
  331. domfexclude (int argc, char *argv[], void *p OPTIONAL)
  332. {
  333. struct no_mf *nm;
  334. int i;
  335.  
  336.     if (argc == 1) {    /*just list them*/
  337.         for (nm = No_mf; nm != NULLMF; nm = nm->next)
  338.             tprintf ("%s ", nm->area);
  339.         tputc ('\n');
  340.     } else {        /*add some call(s)*/
  341.         for (i = 1; i < argc; i++) {
  342.             if (strlen (argv[i]) > 8) {
  343.                 tprintf ("Invalid: %s\n", argv[i]);
  344.                 continue;
  345.             }
  346.             nm = callocw (1, sizeof (struct no_mf));
  347.  
  348.             strncpy (nm->area, argv[i], 20);
  349.             /* add to list */
  350.             nm->next = No_mf;
  351.             No_mf = nm;
  352.         }
  353.     }
  354.     return 0;
  355. }
  356.  
  357.  
  358. #ifdef HOLDMONITOR
  359. static int domfhmode (int argc, char *argv[], void *p);
  360. static int domfhclear (int argc, char *argv[], void *p);
  361. static int domfhstatus (int argc, char *argv[], void *p);
  362. #ifdef SOUNDS
  363. static int domfhsound (int argc, char *argv[], void *p);
  364. #endif
  365.  
  366. static struct cmds MFHOLDtab[] =
  367. {
  368.     { "clear",    domfhclear,    0, 0, NULLCHAR },
  369.     { "mode",    domfhmode,    0, 0, NULLCHAR },
  370. #ifdef SOUNDS
  371.     { "sound",    domfhsound,    0, 0, NULLCHAR },
  372. #endif
  373.     { "status",    domfhstatus,    0, 0, NULLCHAR },
  374.     { NULLCHAR,    NULL,        0, 0, NULLCHAR }
  375. };
  376.  
  377.  
  378. int
  379. domfhold (int argc, char *argv[], void *p)
  380. {
  381.     return subcmd (MFHOLDtab, argc, argv, p);
  382. }
  383.  
  384.  
  385. int
  386. domfhclear (int argc OPTIONAL, char *argv[] OPTIONAL, void *p OPTIONAL)
  387. {
  388.     HOLDindicator = 0;
  389. #ifdef XSERVER
  390.     xnotify (X_HOLD);
  391. #endif
  392.     return 0;
  393. }
  394.  
  395.  
  396. int
  397. domfhmode (int argc, char *argv[], void *p OPTIONAL)
  398. {
  399.     return setbool (&HOLDmode, "Monitoring for HELD messages", argc, argv);
  400. }
  401.  
  402.  
  403. int
  404. domfhstatus (int argc, char *argv[], void *p OPTIONAL)
  405. {
  406.     return setbool (&HOLDindicator, "HELD messages status", argc, argv);
  407. }
  408.  
  409.  
  410. #ifdef SOUNDS
  411. int
  412. domfhsound (int argc, char *argv[], void *p OPTIONAL)
  413. {
  414.     return (setsoundstr (argc, argv, &HOLDsound));
  415. }
  416.  
  417. #endif /* SOUNDS */
  418.  
  419. #endif /* HOLDMONITOR */
  420.  
  421.  
  422. #ifdef ALERTMONITOR
  423. static int domfaclear (int argc, char *argv[], void *p);
  424. static int domfambox (int argc, char *argv[], void *p);
  425. static int domfamode (int argc, char *argv[], void *p);
  426. static int domfastatus (int argc, char *argv[], void *p);
  427. #ifdef SOUNDS
  428. static int domfasound (int argc, char *argv[], void *p);
  429. #endif
  430.  
  431.  
  432. static struct cmds MFALERTtab[] =
  433. {
  434.     { "clear",    domfaclear,    0, 0, NULLCHAR },
  435.     { "mbox",    domfambox,    0, 0, NULLCHAR },
  436.     { "mode",    domfamode,    0, 0, NULLCHAR },
  437. #ifdef SOUNDS
  438.     { "sound",    domfasound,    0, 0, NULLCHAR },
  439. #endif
  440.     { "status",    domfastatus,    0, 0, NULLCHAR },
  441.     { NULLCHAR,    NULL,        0, 0, NULLCHAR }
  442. };
  443.  
  444.  
  445. int
  446. domfalert (int argc, char *argv[], void *p)
  447. {
  448.     return subcmd (MFALERTtab, argc, argv, p);
  449. }
  450.  
  451.  
  452. int
  453. domfaclear (int argc OPTIONAL, char *argv[] OPTIONAL, void *p OPTIONAL)
  454. {
  455.     ALERTindicator = 0;
  456. #ifdef XSERVER
  457.     xnotify (X_ALERT);
  458. #endif
  459.     return 0;
  460. }
  461.  
  462.  
  463. int
  464. domfamode (int argc, char *argv[], void *p OPTIONAL)
  465. {
  466.     return setbool (&ALERTmode, "Alerting of incoming messages to defined areas", argc, argv);
  467. }
  468.  
  469.  
  470. int
  471. domfastatus (int argc, char *argv[], void *p OPTIONAL)
  472. {
  473.     return setbool (&ALERTindicator, "Incoming message alert", argc, argv);
  474. }
  475.  
  476.  
  477. int
  478. domfambox (int argc, char *argv[], void *p OPTIONAL)
  479. {
  480. struct mfalert *mf;
  481. int i;
  482.  
  483.     if (argc == 1) {    /*just list them*/
  484.         for (mf = mf_alerts; mf != NULLMFA; mf = mf->next)
  485.             tprintf ("%s ", mf->area);
  486.         tputc ('\n');
  487.     } else {        /*add some area(s)*/
  488.         for (i = 1; i < argc; i++) {
  489.             if (strlen (argv[i]) > 8) {
  490.                 tprintf ("Invalid: %s\n", argv[i]);
  491.                 continue;
  492.             }
  493.             mf = callocw (1, sizeof (struct mfalert));
  494.  
  495.             strncpy (mf->area, argv[i], 20);
  496.             /* add to list */
  497.             mf->next = mf_alerts;
  498.             mf_alerts = mf;
  499.         }
  500.     }
  501.     return 0;
  502. }
  503.  
  504.  
  505. #ifdef SOUNDS
  506. int
  507. domfasound (int argc, char *argv[], void *p OPTIONAL)
  508. {
  509.     return (setsoundstr (argc, argv, &ALERTsound));
  510. }
  511.  
  512. #endif /* SOUNDS */
  513.  
  514.  
  515. void
  516. alertarea (char *area)
  517. {
  518. struct mfalert *mf;
  519.  
  520.     if (!ALERTmode)
  521.         return;
  522.     for (mf = mf_alerts; mf != NULLMFA; mf = mf->next) {
  523.         if (!stricmp (area, mf->area)) {
  524.             ALERTindicator = 1;
  525. #ifdef XSERVER
  526.             xnotify (X_ALERT);
  527. #endif
  528. #ifdef SOUNDS
  529.             (void) playsound (ALERTsound);
  530. #endif
  531.         }
  532.     }
  533.  
  534. }
  535.  
  536. #endif /* ALERTMONITOR */
  537.  
  538. #endif /* AX25 */
  539. #endif /* MAILFOR */
  540.  
  541.  
  542. /*************************************************************************/
  543.  
  544. int BBSlookup (char *bbs);
  545.  
  546. struct bbs MyFwds[NUMFWDBBS];
  547. int Numfwds = 0;
  548.  
  549. #ifdef RLINE
  550.  
  551. /* Depending on the flag set, the mailbox will
  552.  * read the message's original date,
  553.  * the correct 'from' address (instead of the user%forwardbbs@myhost),
  554.  * and for buls set the X-Forwarded options to prevent
  555.  * unneccesary forward attempts
  556.  * all from the R: lines supplied by the bbs system
  557.  * 920311 - WG7J
  558.  */
  559. static int dobulrdate (int argc, char *argv[], void *p);
  560. static int dorreturn (int argc, char *argv[], void *p);
  561. static int dombloophold (int argc, char *argv[], void *p);
  562. static int dofwdcheck (int argc, char *argv[], void *p);
  563. static int dombhold (int argc, char *argv[], void *p);
  564.  
  565. int Rdate = 0;
  566. int Rreturn = 0;
  567. int Rfwdcheck = 1;
  568.  
  569. static struct cmds Rlinetab[] =
  570. {
  571.     { "check",    dofwdcheck,    0, 0, NULLCHAR },
  572.     { "date",    dobulrdate,    0, 0, NULLCHAR },
  573.     { "hold",    dombhold,    0, 0, NULLCHAR },
  574.     { "loophold",    dombloophold,    0, 0, NULLCHAR },
  575.     { "return",    dorreturn,    0, 0, NULLCHAR },
  576.     { NULLCHAR,    NULL,        0, 0, NULLCHAR }
  577. };
  578.  
  579. void ReadFwdBbs (void);
  580.  
  581.  
  582. static int
  583. dobulrdate (int argc, char *argv[], void *p OPTIONAL)
  584. {
  585.     return setbool (&Rdate, "Use R: for orig. date", argc, argv);
  586. }
  587.  
  588.  
  589. int Mbloophold = 2;
  590.  
  591. /* set loop detection threshold - WG7J */
  592. static int
  593. dombloophold (int argc, char *argv[], void *p OPTIONAL)
  594. {
  595.     return setint (&Mbloophold, "Loop hold after", argc, argv);
  596. }
  597.  
  598.  
  599. static int
  600. dorreturn (int argc, char *argv[], void *p OPTIONAL)
  601. {
  602.     return setbool (&Rreturn, "Use R: for ret. addr.", argc, argv);
  603. }
  604.  
  605.  
  606. static int
  607. dofwdcheck (int argc, char *argv[], void *p OPTIONAL)
  608. {
  609. register int i;
  610.  
  611.     (void) setbool (&Rfwdcheck, "Use R: to check buls", argc, argv);
  612.     if ((argc == 1) && Rfwdcheck && Numfwds) {    /*list the bbses we check*/
  613.         tputs ("Checking for:");
  614.         for (i = 0; i < Numfwds; i++)
  615.             tprintf (" %s", MyFwds[i].name);
  616.         tputc ('\n');
  617.     } else {
  618.         if (Rfwdcheck)
  619.             ReadFwdBbs ();
  620.     }
  621.     return 0;
  622. }
  623.  
  624.  
  625. static int
  626. dombhold (int argc, char *argv[], void *p OPTIONAL)
  627. {
  628.     return setbool (&MbHolding, "Hold local bulletins for review", argc, argv);
  629. }
  630.  
  631.  
  632. int
  633. dombrline (int argc, char **argv, void *p)
  634. {
  635.     return subcmd (Rlinetab, argc, argv, p);
  636. }
  637.  
  638.  
  639. #endif
  640.  
  641.  
  642. #if defined(RLINE) || defined(MBFWD)
  643. int
  644. indexFwdBbs (char *name)
  645. {
  646. int k;
  647.  
  648.     for (k = 0; k < Numfwds; k++) {
  649.         if (!stricmp (MyFwds[k].name, name))
  650.             break;
  651.     }
  652.     if (k == Numfwds)
  653.         k = NUMFWDBBS;
  654.     return k;
  655. }
  656.  
  657.  
  658. void
  659. ReadFwdBbs (void)
  660. {
  661. struct fwdbbs *f;
  662.  
  663. #if 0
  664.     Numfwds = 0;        /* reset - doing this breaks the 'for laston' command */
  665. #endif
  666.     while (Numfwds < NUMFWDBBS) {
  667.         f = fwdread (NULLCHAR, Numfwds);    /* get first BBS entry */
  668.         if (f == NULLFWDBBS)
  669.             break;
  670.         if (indexFwdBbs (f->name) == NUMFWDBBS) {
  671.             MyFwds[Numfwds].subchannel = f->subchannel;
  672.             MyFwds[Numfwds].laston = (time_t) 0;
  673.             strncpy (MyFwds[Numfwds++].name, strupr (f->name), FWDBBSLEN);
  674.         }
  675. #ifdef MBFWD
  676.         fwdfree (&f);
  677. #endif
  678.     }
  679.  
  680.     return;
  681. }
  682.  
  683. #endif /* RLINE */
  684.  
  685.  
  686. int
  687. BBSlookup (char *bbs)
  688. {
  689. int retval;
  690.  
  691.     for (retval = 0; retval < Numfwds; retval++)
  692.         if (!stricmp (MyFwds[retval].name, bbs))
  693.             break;
  694.     if (retval == Numfwds)
  695.         return -1;
  696.     else
  697.         return retval;
  698. }
  699.